package com.gitee.l0km.aocache;

import java.lang.reflect.Modifier;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.google.common.base.Throwables;
import com.google.common.base.Ticker;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.Weigher;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.gitee.l0km.aocache.AocacheUtils.newInstance;
/**
 * 基于GUAVA {@link LoadingCache}实现类型转换接口{@link ThrowingFunction}的缓存包装类。
 * 主要功能：
 * <ul>
 *   <li>缓存计算结果，避免重复计算</li>
 *   <li>支持异常捕获和缓存</li>
 *   <li>提供线程安全的缓存访问</li>
 *   <li>支持自定义缓存策略</li>
 * </ul>
 *
 * <p>使用场景：
 * <ul>
 *   <li>输入值与结果恒定对应的场景</li>
 *   <li>计算成本高的操作</li>
 *   <li>需要缓存异常结果的场景</li>
 * </ul>
 *
 * <p>核心方法：
 * <ul>
 *   <li>{@link #get(Object)} - 获取缓存值，不存在时计算并缓存</li>
 *   <li>{@link #getUncached(Object)} - 直接计算不缓存</li>
 *   <li>{@link #computeIfAbsent(Object, ThrowingFunction)} - 原子性计算并缓存</li>
 * </ul>
 *
 * <p>线程安全：
 * 所有方法都是线程安全的，可以安全地在多线程环境下使用。
 *
 * @author guyadong
 *
 * @param <K> 输入参数类型
 * @param <V> 输出参数类型
 */
class ThrowingFunctionCached<K,V> {
	private static final Object NULL = new Object();
	/** 封装调用抛出的异常 */
	private static class Error{
		final Throwable err;
		private Error(Throwable err) {
			this.err = err;
		}		
	}
	@SuppressWarnings("rawtypes")
	private static final ThrowingFunction  NUL_FUNCTION = new ThrowingFunction() {

		@Override
		public Object apply(Object t) throws Throwable {
			return null;
		}};
	/**
	 * 输入值与计算结果的映射，计算结果可能是正常计算出的V值,也可能是异常(Exception)
	 */
	protected final LoadingCache<K, Object> cache;
	protected final ThrowingFunction<K,V,Throwable> getterFunction;
	protected final V defaultValue;
	/**
	 * 构造方法
	 * @param getterFunction 原类型转换接口实现
	 * @param defaultValue    K为{@code null}时返回的默认值
	 * @param cacheBuilder    外部提供的CacheBuilder实例,为{@code null}则忽略
	 */
	@SuppressWarnings("unchecked")
	public ThrowingFunctionCached(final ThrowingFunction<K, V, Throwable> getterFunction, V defaultValue, CacheBuilder<Object, Object> cacheBuilder) {
		/** 优先使用当前类实现的 ThrowingFunction接口作为 getterFunction */
		if(this instanceof ThrowingFunction) {
			this.getterFunction = (ThrowingFunction<K, V, Throwable>) this;
		}else {
			this.getterFunction = checkNotNull(getterFunction,"getterFunction is null");
		}
		/**  输入参数不可以是缓存实例  */
		checkArgument(!(getterFunction instanceof ThrowingFunctionCached),"getterFunction must not be instance of %s",getClass().getName());
		this.defaultValue = defaultValue;
		if(null == cacheBuilder ) {
			cacheBuilder= CacheBuilder.newBuilder();
		}
		if(this instanceof RemovalListener) {
			/** 如果当前对象实现了RemovalListener接口，自动添加移除监听器 */
			cacheBuilder.removalListener((RemovalListener<Object, Object>) this);
		}
		this.cache = cacheBuilder.build(new CacheLoader<K, Object>(){
			@Override
			public Object load(K key) {
				return computeAsCacheValue(key, ThrowingFunctionCached.this.getterFunction);
			}});
	}
	/**
	 * 构造方法
	 * @param cacheBuilder    外部提供的CacheBuilder实例,为{@code null}则忽略
	 */
	public ThrowingFunctionCached(CacheBuilder<Object,Object> cacheBuilder){
		this(null,null,cacheBuilder);
	}

	/**
	 * 将缓存存储类型转换为值类型
	 * <ul>
	 *   <li>遇到NULL标记时返回null</li>
	 *   <li>遇到Error封装对象时抛出原始异常</li>
	 *   <li>正常值直接进行类型转换</li>
	 * </ul>
	 * @param cached 缓存存储对象，可能为：
	 * <ul>
	 *   <li>NULL标记对象</li>
	 *   <li>Error异常封装实例</li>
	 *   <li>原始值类型实例</li>
	 * </ul>
	 * @return 转换后的值实例，可能为null
	 * @throws Throwable 当缓存存储的是Error封装对象时抛出原始异常
	 */
	@SuppressWarnings("unchecked")
	protected V asValue(Object cached ) throws Throwable{
		if(NULL == cached) {
			return null;
		}
		if(cached instanceof Error) {
			throw ((Error)cached).err;
		}
		return (V) cached;
	}
    /**
     * 将缓存存储类型转换为值类型，若遇到异常则返回null。
     * <p>
     * 处理逻辑如下：
     * <li> 若缓存存储对象为NULL标记，则返回null。</li>
     * <li> 若缓存存储对象为Error封装对象，返回null。</li>
     * <li> 对于正常值，直接进行类型转换。</li>
     * </p>
     *
     * @param cached 缓存存储对象，可能为：
     *               <ul>
     *               <li>NULL标记对象</li>
     *               <li>Error异常封装实例</li>
     *               <li>原始值类型实例</li>
     *               </ul>
     * @return 转换后的值实例，可能为null
     */
    @SuppressWarnings("unchecked")
	protected V asValueOrNull(Object cached ){
		if(NULL == cached) {
			return null;
		}
		if(cached instanceof Error) {
			return null;
		}
		return (V) cached;
	}
    

	/**
	 * 将缓存存储对象转换为原始对象，如果遇到NULL 标记则返回 null。
	 * <p>
	 * 处理逻辑如下：
	 * <ul>
	 * <li> 若缓存存储对象为 NULL 标记，则返回 null。</li>
	 * <li> 若缓存存储对象为 {@link Error} 对象，返回原始异常。</li>
	 * <li> 对于正常值，直接返回。</li>
	 * </ul>
	 * </p>
	 *
	 * @param cached 缓存存储对象，可能为：
	 *               <ul>
	 *               <li>NULL 标记对象</li>
	 *               <li>{@link Error} 异常封装实例</li>
	 *               <li>原始值类型实例</li>
	 *               </ul>
	 * @return 转换后的原始对象，可能为 null 或 Throwable 异常对象
	 */
	protected Object asObjectOrNull(Object cached) {
		if (NULL == cached) {
			return null;
		}
		if(cached instanceof Error) {
			return ((Error)cached).err;
		}
		return cached;
	}
	
	/**
	 * 将值转换为缓存存储类型
	 * <ul>
	 *   <li>null值转换为NULL标记对象</li>
	 *   <li>Throwable实例封装为Error对象</li>
	 *   <li>正常值保持原样存储</li>
	 * </ul>
	 * @param value 输入值，可能为：
	 * <ul>
	 *   <li>null</li>
	 *   <li>Throwable异常实例</li>
	 *   <li>普通值实例</li>
	 * </ul>
	 * @return 缓存存储对象，可能为：
	 * <ul>
	 *   <li>NULL标记对象</li>
	 *   <li>Error异常封装实例</li>
	 *   <li>原始值类型实例</li>
	 * </ul>
	 */
	protected Object asCached(Object value ){
		if(null == value) {
			return NULL;
		}
		if(value instanceof Throwable) {
			return new Error((Throwable)value);
		}
		return value;
	}	
	
	/**
	 * 缓存调用
	 * @param key
	 * @return {@code key}为{@code null}返回{@link #defaultValue}
	 * @throws Throwable 
	 */
	public V get(K key) throws Throwable{
		if(null != key){
			return asValue(cache.getUnchecked(key));
		}
		return defaultValue;
	}

    /**
     * 刷新指定键的缓存值。<p>
     * 此方法会异步地重新加载指定键的缓存值。如果缓存中存在该键，则会在后台线程中重新计算其值，
     * 并在计算完成后更新缓存。如果缓存中不存在该键，则不会进行任何操作。<p>
     * 刷新操作是异步的，因此调用此方法不会阻塞当前线程。<p>
     * 
     * @param key 要刷新的缓存键
     */
    protected void refresh(K key) {
		cache.refresh(key);
	}
	/**
     * 根据键获取缓存中的值，如果不存在或缓存为异常对象则返回 null
     * <p>
     * 此方法用于安全地获取缓存中的值，避免因缓存中存储的异常对象而抛出异常。
     * <li> 如果键为 null，方法直接返回 null。</li>
     * <li> 如果键存在且对应的值不是异常对象，则返回该值。</li>
     * <li> 如果键存在但对应的值是异常对象，或者键不存在，则返回 null。</li>
     * </p>
     * 注意：如果指定的键不存在，不会触发缓存的加载（计算）操作。
     * @param key 缓存键
     * @return 如果键存在且值不是异常对象，则返回对应的值；否则返回 null
	 * @see #asValueOrNull(Object)
     */
    protected V getValueOrNull(K key){
		return null == key ? null : asValueOrNull(cache.getIfPresent(key));
	}

	/**
	 * 获取指定键的缓存存储对象，如果缓存中不存在该键，则返回 null。<p>
	 * 此方法不会触发缓存的加载（计算）操作。<br>
	 * 若传入的键为 null，则直接返回 null。<p>
	 * 返回的对象可能是以下类型之一：
	 * <ul>
	 * <li>NULL 标记对象</li>
	 * <li>{@link Error} 异常封装实例</li>
	 * <li>原始值类型实例</li>
	 * </ul>
	 * 
	 * @param key 要获取缓存的键
	 * @return 缓存存储对象，如果键不存在或为 null 则返回 null
	 */
	protected Object getCachedIfPresent(K key){
    	return null == key ? null : cache.getIfPresent(key);
    }

	/**
	 * 计算给定键的值并将其转换为缓存存储类型。
	 * <p>
	 * 此方法使用提供的{@code throwingFunction}计算给定键的值，并将结果转换为适合缓存存储的类型。
	 * 如果计算过程中抛出异常，异常将被封装为{@link Error}对象并返回。
	 * </p>
	 *
	 * @param <E> 计算函数可能抛出的异常类型
	 * @param key 要计算的键
	 * @param throwingFunction 用于计算值的函数
	 * @return 计算结果的缓存存储类型，可能是：
	 * <ul>
	 *   <li>NULL标记对象</li>
	 *   <li>Error异常封装实例</li>
	 *   <li>原始值类型实例</li>
	 * </ul>
	 */
	private <E extends Throwable> Object computeAsCacheValue(K key, ThrowingFunction<K, V, E> throwingFunction) {
		try {
			// 尝试计算键的值并转换为缓存存储类型
			return asCached(throwingFunction.apply(key));
		} catch (Throwable e) {
			// 若计算过程中抛出异常，将异常转换为缓存存储类型
			return asCached(e);
		}
	}
	/**
	 * 安全地将键值对存入缓存
	 * <p>对应缓存更新策略：{@link UpdateStrategy#ALWAYS}
	 * @param key 缓存键
	 * @param value 缓存值
	 */
	protected void put(K key, V value){
		cache.asMap().put(key, asCached(value));
	}

	/**
	 * 替换指定键的缓存值
	 * <p>静默处理所有替换操作中的异常情况<p>
	 * 对应缓存更新策略：{@link UpdateStrategy#REPLACE}
	 * @param key 需要替换的缓存键
	 * @param value 要替换的新值
	 */
	protected void replace(K key, V value){
		if (null != cache.getIfPresent(key)) {
			synchronized (cache) {
				if (null != cache.getIfPresent(key)) {
					put(key, value);
				}
			}
		}
	}
	/**
	 * 安全移除指定键的缓存项
	 * <p>静默处理所有移除操作中的异常情况<p>
	 * 对应缓存更新策略：{@link UpdateStrategy#REMOVE}
	 * @return 移除的缓存项的值，如果不存在则返回null
	 */
	protected Object remove(K key){
		return cache.asMap().remove(key);
	}

	/**
	 * 清空缓存<br>
	 * 
	 * 此方法将清空cache中的所有数据它通过将cache视图为映射，并调用clear方法来实现
	 * 这是一个受保护的方法，意味着它只能在该类内部或其子类中访问
	 */
	protected void clear(){
	    cache.asMap().clear();
	}
	/**
	 * 清理缓存中的过期条目和引用。<p>
	 * 此方法执行缓存的维护操作，尝试移除所有已过期的条目，
	 * 并释放不再被引用的资源。<p>
	 * 注意，该操作是尽力而为的，并不保证能立即移除所有过期条目。
	 */
	void cleanUp() {
		cache.cleanUp();
	}
	/**
	 * 获取缓存中当前存储的键值对数量。<p>
	 * 该方法通过访问内部的 {@link LoadingCache} 实例，调用其 {@code size()} 方法来获取缓存的大小。<p>
	 * 返回值表示当前缓存中存储的键值对的数量，该值可能会随着缓存的操作（如添加、移除等）而变化。<br>
	 * 注意：由于缓存的并发特性，返回值可能不是实时准确的。
	 * @return 缓存中当前存储的键值对数量
	 */
	protected long size(){
		return cache.size();
	}
	/**
	 * 获取缓存中所有键的集合。<p>
	 * 该方法通过将内部的 {@link LoadingCache} 转换为映射视图，然后调用其 {@code keySet()} 方法来获取所有键的集合。<p>
	 * 返回的集合是一个实时视图，会随着缓存中键的添加或移除而动态变化。<br>
	 * 注意：由于缓存的并发特性，在遍历返回的集合时，可能会遇到缓存数据的变化，因此建议在遍历时进行适当的同步处理。<p>
	 * 
	 * @return 包含缓存中所有键的集合
	 */
	protected Set<K> keySet() {
		return cache.asMap().keySet();
	}
	public static<K, V>Builder<K, V> builder(){
		return new Builder<>();
	}
	/**
	 * builder for ThrowingFunctionCached
	 *
	 * @param <K>
	 * @param <V>
	 */
	public static class Builder<K, V>{
		final CacheBuilder<Object, Object> cacheBuilder;
		ThrowingFunction<K,V,Throwable> getterFunction;
		private V defaultValue;

		protected Builder() {
			this.cacheBuilder = CacheBuilder.newBuilder();
		}

		protected Builder(CacheBuilder<Object, Object> cacheBuilder, ThrowingFunction<K,V,Throwable> getterFunction, V defaultValue) {
			this.cacheBuilder = cacheBuilder;
			this.getterFunction = getterFunction;
			this.defaultValue = defaultValue;
		}
		@SuppressWarnings("unchecked")
		public<K1, V1> Builder<K1, V1> getterFunction( ThrowingFunction<K1,V1,Throwable> getterFunction){
			return new Builder<K1, V1>(this.cacheBuilder,checkNotNull(getterFunction,"getterFunction is null"),(V1)defaultValue);
		}
		public <K1, V1> Builder<K1, V1> getterFunction( ThrowingFunction<K1,V1,Throwable> getterFunction,V1 defaultValue){
			return new Builder<K1, V1>(this.cacheBuilder,checkNotNull(getterFunction,"getterFunction is null"),defaultValue);
		}

		/**
		 * 指定指{@link #getterFunction}使用恒返回{@code null}的{@code ThrowingFunction}实例
		 * @since 2.9.10
		 */
		@SuppressWarnings("unchecked")
		public Builder<K, V> nullGetterFunction() {
			this.getterFunction = (ThrowingFunction<K,V,Throwable>)NUL_FUNCTION;
			return this;
		}
		/**
		 * 指定当{@link #getterFunction}返回值为{@code null}或{@link ThrowingFunctionCached#get(Object)}参数为{@code null}时返回的默认值,
		 * 默认为{@code null}
		 * @param defaultValue
		 */
		public Builder<K, V> defaultValue(V defaultValue) {
			this.defaultValue = defaultValue;
			return this;
		}
		/**
		 * @see CacheBuilder#initialCapacity(int)
		 */
		public Builder<K, V> initialCapacity(int initialCapacity) {
			cacheBuilder.initialCapacity(initialCapacity);
			return this;
		}

		/**
		 * @see CacheBuilder#concurrencyLevel(int)
		 */
		public Builder<K, V> concurrencyLevel(int concurrencyLevel) {
			cacheBuilder.concurrencyLevel(concurrencyLevel);
			return this;
		}

		/**
		 * @see CacheBuilder#maximumSize(long)
		 */
		public Builder<K, V> maximumSize(long size) {
			cacheBuilder.maximumSize(size);
			return this;
		}

		/**
		 * @see CacheBuilder#maximumWeight(long)
		 */
		public Builder<K, V> maximumWeight(long weight) {
			cacheBuilder.maximumWeight(weight);
			return this;
		}

		/**
		 * @see CacheBuilder#weigher(Weigher)
		 */
		public <K1, V1> Builder<K, V> weigher(Weigher<? super K1, ? super V1> weigher) {
			cacheBuilder.weigher(weigher);
			return this;
		}

		public Builder<K, V> weakKeys() {
			cacheBuilder.weakKeys();
			return this;
		}

		/**
		 * @see CacheBuilder#weakValues()
		 */
		public Builder<K, V> weakValues() {
			cacheBuilder.weakValues();
			return this;
		}

		/**
		 * @see CacheBuilder#softValues()
		 */
		public Builder<K, V> softValues() {
			cacheBuilder.softValues();
			return this;
		}

		/**
		 * @see CacheBuilder#expireAfterWrite(long, TimeUnit)
		 */
		public Builder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
			cacheBuilder.expireAfterWrite(duration, unit);
			return this;
		}

		/**
		 * @see CacheBuilder#expireAfterAccess(long, TimeUnit)
		 */
		public Builder<K, V> expireAfterAccess(long duration, TimeUnit unit) {
			cacheBuilder.expireAfterAccess(duration, unit);
			return this;
		}

		/**
		 * @see CacheBuilder#refreshAfterWrite(long, TimeUnit)
		 */
		public Builder<K, V> refreshAfterWrite(long duration, TimeUnit unit) {
			cacheBuilder.refreshAfterWrite(duration, unit);
			return this;
		}

		/**
		 * @see CacheBuilder#ticker(Ticker)
		 */
		public Builder<K, V> ticker(Ticker ticker) {
			cacheBuilder.ticker(ticker);
			return this;
		}

		/**
		 * @see CacheBuilder#removalListener(RemovalListener)
		 */
		public Builder<K, V> removalListener(RemovalListener<K, V> listener) {
			cacheBuilder.removalListener(listener);
			return this;
		}
		/**
		 * @see CacheBuilder#recordStats()
		 */
		public Builder<K, V> recordStats() {
			cacheBuilder.recordStats();
			return this;
		}

		public ThrowingFunctionCached<K,V> build() {
			return new ThrowingFunctionCached<>(getterFunction, defaultValue, cacheBuilder);
		}
		/**
		 * 将当前实例作为参数反射构建指定类型的实例<br>
		 * 要求目标类型构造方法第一个参数类型必须是{@link Builder}<p>
		 * 如果 {@code otherArgs}不为空要求构造方法第二个及以后的所有参数的类型能匹配{@code otherArgs}的类型，
		 * 否则抛出异常<p>
		 * @param <C>目标类型变量
		 * @param targetClass 目标类型
		 * @param otherCtorArgs 其他构造方法参数，为{@code null}或空忽略,否则要求数组元素不能为{@code null}
		 */
		public <K1,V1,C extends ThrowingFunctionCached<K1, V1>& ThrowingFunction<K1, V1, Throwable>> C 
			build(Class<C> targetClass,Object...otherCtorArgs) 	 {
			try {
				checkArgument(targetClass != null,"targetClass is null");
				if(null == otherCtorArgs || 0 == otherCtorArgs.length) {
					return newInstance(targetClass, new Object[] {this});
				}else {
					for(int i=0;i<otherCtorArgs.length;i++){
						checkNotNull(otherCtorArgs[i],"otherCtorArgs[%s] is null ",i);
					}
					if(Modifier.isStatic(targetClass.getModifiers()) || null==targetClass.getEnclosingClass()) {
						Object[] args = new Object[otherCtorArgs.length+1];
						args[0] = this;
						System.arraycopy(otherCtorArgs, 0, args, 1, otherCtorArgs.length);
						return newInstance(targetClass,args);
					}else {
						/**  非静态类 */
						Object[] args = new Object[otherCtorArgs.length+1];
						args[0] = otherCtorArgs[0];
						args[1]=this;
						System.arraycopy(otherCtorArgs, 1, args, 2, otherCtorArgs.length-1);
						return newInstance(targetClass,args);
					}
				}
			} catch (Exception e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			} 
		}
	}
}
